package com.own.component.mybatis.aspect;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.own.component.common.base.entity.po.BaseBean;
import com.own.component.common.base.entity.po.BasePo;
import com.own.component.common.em.RequestEnum;
import com.own.component.common.exception.BusinessException;
import com.own.component.common.login.parent.ParentAuthenticationLoginUser;
import com.own.component.common.login.user.BaseLoginUser;
import com.own.component.common.login.user.DefaultLoginUser;
import com.own.component.mybatis.property.MybatisProperty;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;

/**
 * CustomMapperAspect
 *
 * @author chenxueli
 * @date 2023-08-29 17:05:30
 */
@Slf4j
@Aspect
@Component
@Order(1)
public class CustomMapperAspect {

    @Resource
    private MybatisProperty mybatisProperty;

    @Resource
    private ParentAuthenticationLoginUser parentAuthenticationLoginUser;

    @Pointcut("execution(* com.own.component.common.base.mapper.BaseMapper.selectOne(..))")
    public void pointSelectOneService() {
    }

    @Pointcut("execution(* com.own.component.common.base.mapper.BaseMapper.insert(..))")
    public void pointInsertService() {
    }

    @Pointcut("execution(* com.own.component.common.base.mapper.BaseMapper.insertBatch(..))")
    public void pointInsertBatchService() {
    }

    @Pointcut("execution(* com.own.component.common.base.mapper.BaseMapper.update(..))")
    public void pointUpdateService() {
    }

    @Pointcut("execution(* com.own.component.common.base.mapper.BaseMapper.updateById(..))")
    public void pointUpdateVersionService() {
    }

    @Pointcut("execution(* com.own.component.common.base.mapper.BaseMapper.delete(..))")
    public void pointDeleteService() {
    }

    public BaseLoginUser loginUser() {
        try {
            return parentAuthenticationLoginUser.loginUser();
        } catch (Exception e) {
            return DefaultLoginUser.DEFAULT;
        }
    }

    /**
     * 查询单个数据的时候异常修改
     *
     * @param point 切点
     */
    @Before("pointSelectOneService()")
    @SuppressWarnings("all")
    public <T extends BaseBean> void beforeSelectOneService(JoinPoint point) {
        var object = point.getArgs()[0];
        if (object == null) {
            return;
        } else if (object instanceof QueryWrapper) {
            var wrapper = (QueryWrapper<T>) object;
            wrapper.last("limit 1");
        } else if (object instanceof LambdaQueryWrapper) {
            var wrapper = (LambdaQueryWrapper<T>) object;
            wrapper.last("limit 1");
        }
    }

    /**
     * 新增mapper，添加删除标记、乐观锁、创建时间
     *
     * @param point 切点
     */
    @Before("pointInsertService()")
    public void beforeInsertService(JoinPoint point) {
        if (!mybatisProperty.isInsert()) {
            return;
        }
        var loginUser = loginUser();
        for (var object : point.getArgs()) {
            handleBean(object, loginUser);
        }
    }

    /**
     * 批量新增mapper，添加删除标记、乐观锁、创建时间
     *
     * @param point 切点
     */
    @Before("pointInsertBatchService()")
    public void beforeInsertBatchService(JoinPoint point) {
        if (!mybatisProperty.isInsertBatch()) {
            return;
        }
        var collection = (List<?>) point.getArgs()[0];
        // 转回list对象
        if (collection.isEmpty()) {
            return;
        }
        var loginUser = loginUser();
        // 获取对象类型
        for (var object : collection) {
            handleBean(object, loginUser);
        }
    }

    /**
     * 修改mapper扩展，添加修改时间,乐观锁验证
     *
     * @param point 切点
     * @return 对象
     */
    @Around(value = "pointUpdateVersionService()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        if (!mybatisProperty.isUpdate()) {
            return point.proceed();
        }
        BasePo po = null;
        var loginUser = loginUser();
        for (var object : point.getArgs()) {
            if (object instanceof BasePo) {
                po = (BasePo) object;
                po.setUpdateUserId(loginUser.userId());
                po.setUpdateUserName(loginUser.userName());
                po.setGmtModified(LocalDateTime.now());
            }
        }
        var res = point.proceed();
        if (res instanceof Long result) {
            if (result == 0 && po != null && po.getVersion() != null) {
                throw new BusinessException(RequestEnum.VERSION_NOT_MATCH);
            }
        } else if (res instanceof Integer result) {
            if (result == 0 && po != null && po.getVersion() != null) {
                throw new BusinessException(RequestEnum.VERSION_NOT_MATCH);
            }
        }
        return res;
    }

    /**
     * 删除mapper，添加修改时间
     *
     * @param point 切点
     */
    @Before("pointDeleteService()")
    public void pointDeleteService(JoinPoint point) {
        if (!mybatisProperty.isDelete()) {
            return;
        }
        var loginUser = loginUser();
        for (var object : point.getArgs()) {
            if (object instanceof BasePo po) {
                po.setGmtModified(LocalDateTime.now());
                po.setUpdateUserId(loginUser.userId());
                po.setUpdateUserName(loginUser.userName());
            }
        }
    }

    /**
     * 数据库数据插入异常 sql异常捕获
     *
     * @param point 切点
     * @param error 异常信息
     */
    @AfterThrowing(pointcut = "pointInsertService() || pointInsertBatchService()", throwing = "error")
    public void throwingInsertSql(JoinPoint point, Throwable error) {
        log.error("插入数据异常:{}", error.getMessage(), error);
        throw new BusinessException(RequestEnum.SQL_INSERT_ERROR, error);
    }

    /**
     * 数据库数据更新异常 sql异常捕获
     *
     * @param point 切点
     * @param error 异常信息
     */
    @AfterThrowing(pointcut = "pointUpdateService()", throwing = "error")
    public void throwingUpdateSql(JoinPoint point, Throwable error) {
        throw new BusinessException(RequestEnum.SQL_UPDATE_ERROR, error);
    }

    /**
     * 数据库数据删除异常 sql异常捕获
     *
     * @param point 切点
     * @param error 异常信息
     */
    @AfterThrowing(pointcut = "pointDeleteService()", throwing = "error")
    public void throwingDeleteSql(JoinPoint point, Throwable error) {
        throw new BusinessException(RequestEnum.SQL_DELETE_ERROR, error);
    }

    /**
     * 处理bean数据
     *
     * @param object 插入对象
     */
    private void handleBean(Object object, BaseLoginUser loginUser) {
        // 判断是否是BaseBean类型
        if (object instanceof BaseBean bean) {
            bean.init(loginUser);
            bean.beforeInsert();
        }
    }

}
